home *** CD-ROM | disk | FTP | other *** search
/ CDUTIL 13 / CDUTIL #13 Julio 1995.iso / windows / acadcom / ads / sample / tadc.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-02-08  |  25.4 KB  |  811 lines

  1. /* Next available MSG number is  33 */
  2.  
  3. /*    
  4.  
  5.    ADC.C
  6.  
  7.    Copyright (C) 1989, 1990, 1991, 1992, 1993, 1994 by Autodesk, Inc.
  8.  
  9.    Permission to use, copy, modify, and distribute this software in 
  10.    object code form for any purpose and without fee is hereby granted, 
  11.    provided that the above copyright notice appears in all copies and 
  12.    that both that copyright notice and the limited warranty and 
  13.    restricted rights notice below appear in all supporting 
  14.    documentation.
  15.  
  16.    AUTODESK PROVIDES THIS PROGRAM "AS IS" AND WITH ALL FAULTS.  
  17.    AUTODESK SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTY OF 
  18.    MERCHANTABILITY OR FITNESS FOR A PARTICULAR USE.  AUTODESK, INC.
  19.    DOES NOT WARRANT THAT THE OPERATION OF THE PROGRAM WILL BE 
  20.    UNINTERRUPTED OR ERROR FREE.
  21.  
  22.    Use, duplication, or disclosure by the U.S. Government is subject to 
  23.    restrictions set forth in FAR 52.227-19 (Commercial Computer 
  24.    Software - Restricted Rights) and DFAR 252.227-7013(c)(1)(ii) 
  25.    (Rights in Technical Data and Computer Software), as applicable.
  26.     
  27.    .
  28.  
  29.     
  30.     Some example ADS code demonstrating the use of ads_tablet() and
  31.     tablet mode in general.
  32.      
  33.     DIGPASS shows how to establish a "pass-through" tablet
  34.     transformation in any UCS (doing it in world coordinates is
  35.     trivial).  It is used by BILINCAL.
  36.  
  37.     SAMPLE, BILINCAL, and BILINMPT together demonstrate a possible
  38.     implementation of a different tablet transformation than any
  39.     supported directly by AutoCAD.
  40.  
  41.     SAVCAL and RSTCAL constitute a trivial matched pair of functions
  42.     to save the current tablet calibration and subsequently to restore
  43.     it.
  44.  
  45.         Greg Lutz
  46.         May, 1991
  47.  
  48.     The matrix arithmetic in this file holds strictly to the ADS/LISP
  49.     conventions:
  50.  
  51.         1.  m[i][j] is the element in row i, column j of matrix m.
  52.         2.  Transforming vector v by matrix m is done by treating v
  53.             as a column vector and computing m x v.
  54. */
  55.  
  56. #include  <stdio.h>
  57. #include  <math.h>
  58. #include  "adslib.h"
  59.  
  60. /* Special assertion handler for ADS applications. */
  61.  
  62. #ifndef NDEBUG
  63. #define assert(ex) {if (!(ex)){ads_printf( \
  64.                     /*MSG1*/"TADC: Assertion (%s) failed: file \"%s\", \
  65.                     line %d\n", /*MSG0*/" "#ex" ",__FILE__,__LINE__); \
  66.                     ads_abort(/*MSG2*/"Assertion failed.");}}
  67. #else
  68. #define assert(ex)
  69. #endif
  70.  
  71. #ifndef GOOD
  72. #define GOOD    0
  73. #define BAD     (-1)
  74. #endif
  75.  
  76.  
  77. /*  Some macros which we need more generally available somewhere, sometime: */
  78.  
  79. #define TYPSTRUC(size)  typedef struct {char dummy[size];}
  80. #define movecnt(c,t,f)  if (1) {TYPSTRUC(c)*_p_; *(_p_)(t) = *(_p_)(f);} else
  81. #define cpy(s,t,f) movecnt(sizeof(s), t, f)
  82.  
  83. #define cpyname(to, from)   cpy(ads_name, to, from)  /* Copy an ads_name */
  84. #define cpypoint(to, from)  cpy(ads_point, to, from) /* Copy an ads_point */
  85.  
  86.  
  87. /*  The arbaxis crossover point: */
  88. #define ARBBOUND  0.015625            /* aka "1/64" */
  89.  
  90. /*  Saved tablet calibration from savcal() */
  91. static struct resbuf *tcal = NULL;
  92.  
  93.  
  94. /*  Local function declarations */
  95.  
  96. int     digpass     _((void));
  97. int     idigpass    _((void));
  98. int     bilincal    _((void));
  99. int     ibilinmpt   _((ads_real *in, ads_real *out));
  100. int     bilinmpt    _((void));
  101. void    cross       _((ads_real *ap, ads_real *bp, ads_real *cp));
  102. void    matxmat     _((ads_matrix cp, ads_matrix ap, ads_matrix bp));
  103. int     normalize   _((ads_real *ap, ads_real *bp));
  104. void    pswap       _((ads_real *p1, ads_real *p2));
  105. int     sample      _((void));
  106. void    dline       _((char *p1str, char *p2str));
  107. void    dvert       _((char *p1str));
  108. int     savcal      _((void));
  109. int     rstcal      _((void));
  110. void    aperror     _((char *routine));
  111.  
  112.  
  113. /*  Table of externally defined functions */
  114. static struct {
  115.     char *fcn_name;
  116.     int (*fcn) _((void));
  117. } fcn_table[] = {
  118.     {/*MSG0*/"digpass", digpass},
  119.     {/*MSG0*/"c:bilincal", bilincal},
  120.     {/*MSG0*/"bilinmpt", bilinmpt},
  121.     {/*MSG0*/"c:sample", sample},
  122.     {/*MSG0*/"savcal", savcal},
  123.     {/*MSG0*/"rstcal", rstcal},
  124. };
  125. #define NFCN    (sizeof fcn_table/sizeof fcn_table[0])
  126.  
  127.  
  128. /*  Calibration points for bilincal() and bilinmpt(): */
  129. static ads_point t[4], p[4];
  130. static int cbrated = FALSE;           /* Not calibrated yet */
  131. static int bilinsgn;                  /* Sign to use in quadratic */
  132.  
  133.  
  134. /* MAIN - the main routine */
  135.  
  136.  
  137. void
  138. /*FCN*/main(argc, argv)
  139.   int argc;
  140.   char *argv[];
  141. {
  142.     int rqcode, rscode, i;
  143.     char errmsg[80];
  144.  
  145.     ads_init(argc, argv);             /* Initialize the interface */
  146.     rscode = RSRSLT;                  /* Initial request code */
  147.     for ( ;; ) {
  148.         /*  Send link partner a result code, get back a request: */
  149.         if ((rqcode = ads_link(rscode)) < 0) {
  150.  
  151.             sprintf(errmsg, /*MSG31*/"TADC: bad status from ads_link() = %d\n",
  152.                     rqcode);
  153. #ifdef Macintosh
  154.             macalert(errmsg);
  155. #else
  156.             puts(errmsg);
  157.             fflush(stdout);
  158. #endif /* Macintosh */
  159.             exit(1);
  160.         }
  161.  
  162.         /*  Handle the request code  */
  163.         switch (rqcode) {
  164.         case RQXLOAD:
  165.             /*  (Re-)define functions  */
  166.             rscode = RSRSLT;
  167.             for (i = 0; i < NFCN && rscode == RSRSLT; i++)
  168.                 if (ads_defun(fcn_table[i].fcn_name, i) != RTNORM
  169.                     || ads_regfunc(fcn_table[i].fcn, i) != RTNORM   
  170.                    )
  171.                 rscode = RSERR;       /* Result code = error */
  172.             if (rscode == RSRSLT)
  173.                 /*  NOTE!!!  The following line will have unknown
  174.                     consequences if this application is loaded via
  175.                     acad.ads!!!!*/
  176.                 ads_printf(/*MSG32*/"\n\nType SAMPLE to run test application\n");
  177.             break;
  178.         case RQSUBR:
  179.             i = ads_getfuncode();
  180.             rscode = (*fcn_table[i].fcn)();
  181.             break;
  182.         }
  183.     }
  184. }
  185.  
  186.  
  187. /*  DIGPASS  --  External function to establish a tablet transformation
  188.                  such that a tablet point with coordinates (x,y) maps
  189.                  into the point (x,y) in the current UCS.  Returns
  190.                  RSRSLT or RSERR.  */
  191.  
  192. static int
  193. /*FCN*/digpass()
  194. {
  195.     register int stat = idigpass();
  196.     ads_retvoid();
  197.     return stat;
  198. }
  199.  
  200.  
  201.  
  202. /*  IDIGPASS  --  Internal function which does the actual work of
  203.                   DIGPASS.  Returns RSRSLT or RSERR.  */
  204. static int
  205. /*FCN*/idigpass()
  206. {
  207.     ads_matrix uwxform, wlxform, ulxform;
  208.     static ads_point ey = {0.0, 1.0, 0.0},
  209.                      ez = {0.0, 0.0, 1.0};
  210.     ads_point ucszdir;
  211.     struct resbuf uo, ux, uy, *ttl;
  212.     int i;  
  213.  
  214.     /*  Fetch the origin, x-axis direction, and y-axis direction of the
  215.         current UCS, and compute the z-axis direction  */
  216.  
  217.     ads_getvar(/*MSG0*/"UCSORG", &uo);
  218.     ads_getvar(/*MSG0*/"UCSXDIR", &ux);
  219.     ads_getvar(/*MSG0*/"UCSYDIR", &uy);
  220.     cross(ucszdir, ux.resval.rpoint, uy.resval.rpoint);
  221.  
  222.     /*  Use these to build the transformation from UCS to world coords
  223.         (the above axis vectors, and the origin, become the COLUMNS of
  224.         this matrix).  */
  225.  
  226.     for (i = X; i <= Z; i++) {
  227.         uwxform[i][X] = ux.resval.rpoint[i];
  228.         uwxform[i][Y] = uy.resval.rpoint[i];
  229.         uwxform[i][Z] = ucszdir[i];
  230.         uwxform[i][T] = uo.resval.rpoint[i];
  231.     }
  232.  
  233.     /*  Use the AutoCAD "arbaxis" algorithm to construct the transforma-
  234.         tion from world coordinates to the current LCS (determined
  235.         solely by the UCS Z-axis direction) */
  236.  
  237.     if (fabs(ucszdir[X]) < ARBBOUND && fabs(ucszdir[Y]) < ARBBOUND)
  238.         cross(wlxform[X], ey, ucszdir);
  239.     else
  240.         cross(wlxform[X], ez, ucszdir);
  241.     normalize(wlxform[X], wlxform[X]);
  242.     cross(wlxform[Y], ucszdir, wlxform[X]);
  243.     normalize(wlxform[Y], wlxform[Y]);
  244.     cpypoint(wlxform[Z], ucszdir);
  245.     for (i = X; i <= Z; i++)
  246.         wlxform[i][T] = 0.0;          /* No translation */
  247.  
  248.     /*  Compose those two matrices to get the UCS-to-LCS transformation  */
  249.     matxmat(ulxform, wlxform, uwxform);
  250.  
  251.     /*  Transformation's Z-column should be (0,0,1): */
  252.  
  253. #if 0  /* These asserts cause compilation errors on the Sparc... */
  254.     assert(fabs(ulxform[X][Z]) < 1.0E-10);
  255.     assert(fabs(ulxform[Y][Z]) < 1.0E-10);
  256.     assert(fabs(ulxform[Z][Z] - 1.0) < 1.0E-10);
  257. #endif
  258.  
  259.     /*  That's it: if the tablet sends in point (x,y), this matrix will
  260.         act as if x and y were in UCS coords and transform them into the
  261.         corresponding point (u,v) in LCS coords.  AutoCAD needs to turn
  262.         that back into UCS coordinates, and (x,y) is the pair it comes
  263.         up with.  We just need to throw away the Z-column (which we just
  264.         assured is zero anyway) and pass the rows to ads_tablet().  */
  265.  
  266.     ulxform[X][Z] = ulxform[X][T];
  267.     ulxform[Y][Z] = ulxform[Y][T];
  268.     ttl = ads_buildlist(RTSHORT, 1,
  269.                         RT3DPOINT, ulxform[X],
  270.                         RT3DPOINT, ulxform[Y],
  271.                         RT3DPOINT, ez,
  272.                         RT3DPOINT, ucszdir,
  273.                         RTNONE);
  274.     i = ads_tablet(ttl, NULL);        /* Send it to AutoCAD */
  275.     ads_relrb(ttl);
  276.     if (i != RTNORM) {
  277.         aperror(/*MSG0*/"ads_tablet");
  278.         return RSERR;
  279.     } else
  280.         return RSRSLT;
  281. }
  282.  
  283.  
  284.  
  285. /*  Some geometric utility functions  */
  286.  
  287.  
  288. /* CROSS - cross product of two vectors, a = b X c */
  289.  
  290. void
  291. /*FCN*/cross(ap, bp, cp)
  292.   ads_real *ap;
  293.   ads_real *bp, *cp;
  294. {
  295.     ap[X] = bp[Y] * cp[Z] - bp[Z] * cp[Y];
  296.     ap[Y] = bp[Z] * cp[X] - bp[X] * cp[Z];
  297.     ap[Z] = bp[X] * cp[Y] - bp[Y] * cp[X];
  298. }
  299.  
  300.  
  301. /*  MATXMAT -- Multiply two matrices.  The matrix type, ads_matrix,
  302.                may be any array type with at least three rows and four
  303.                columns.  */
  304.  
  305. void
  306. /*FCN*/matxmat(matrixc, matrixa, matrixb)
  307.   ads_matrix matrixc, matrixa, matrixb;
  308. {
  309.     int i, k;
  310.  
  311.     for (i = X; i <= Z; i++)
  312.         for (k = X; k <= Z; k++)
  313.             matrixc[i][k] = matrixa[i][X] * matrixb[X][k] +
  314.                             matrixa[i][Y] * matrixb[Y][k] +
  315.                             matrixa[i][Z] * matrixb[Z][k];
  316.     for (i = X; i <= Z; i++)
  317.         matrixc[i][T] = matrixa[i][X] * matrixb[X][T] +
  318.                         matrixa[i][Y] * matrixb[Y][T] +
  319.                         matrixa[i][Z] * matrixb[Z][T] +
  320.                         matrixa[i][T];
  321. }
  322.  
  323.  
  324. /*  NORMALIZE  --  Normalize a vector: ap = unit vector in direction of bp.
  325.                    Returns BAD if bp is null.  */
  326.  
  327. int
  328. /*FCN*/normalize(ap, bp)
  329.   register ads_real *ap;
  330.   ads_real *bp;
  331. {
  332.     ads_real d;
  333.  
  334.     if ((d = (bp[X] * bp[X] + bp[Y] * bp[Y] + bp[Z] * bp[Z]))
  335.         <= 1.0E-12)
  336.         return BAD;
  337.     d = 1.0 / sqrt(d);
  338.     ap[X] = bp[X] * d;
  339.     ap[Y] = bp[Y] * d;
  340.     ap[Z] = bp[Z] * d;
  341.     return GOOD;
  342. }
  343.  
  344.  
  345.  
  346. /*  BILINCAL  --  Calibrate the digitizer for a bilinear mapping, i.e.,
  347.                   a transformation which maps a quadrilateral on the
  348.                   tablet into a quadrilateral in model space, with edges
  349.                   mapping linearly into edges and interior points
  350.                   mapping as follows: given point P inside the
  351.                   quadrilateral on the tablet, draw a line which passes
  352.                   through P and divides the two quadrilateral edges it
  353.                   intersects in equal ratios.  Draw a line between the
  354.                   two corresponding edges of the target quadrilateral
  355.                   (in model space) which divides those edges in the same
  356.                   ratio as that determined above.  Repeat this
  357.                   operation, constructing a line between the other pair
  358.                   of edges of the tablet quadrilateral, and similarly
  359.                   draw a line between the corresponding edges of the
  360.                   target quadrilateral dividing them in the same ratio.
  361.                   P maps into the point at the intersection of the two
  362.                   lines thus drawn in the target quadrilateral.  This is
  363.                   essentially the 2-space analogue of a Coon's patch.
  364.  
  365.                   BILINCAL just determines the mapping by soliciting the
  366.                   calibration points.  bilinmpt(), below, does the hard
  367.                   work.  Note that, though there is one case in which we
  368.                   complain about degenerate calibration points, there
  369.                   are many other cases we miss entirely.  This code does
  370.                   not purport to be very robust with respect to bad
  371.                   input points.  */
  372.  
  373. static int
  374. /*FCN*/bilincal()
  375. {
  376.     struct resbuf tm;
  377.     int i;
  378.     char sb[100];
  379.     ads_point p11, p12, p0;
  380.  
  381.     /*  Make AutoCAD pass us raw digitizer coordinates  */
  382.  
  383.     if (digpass() != RSRSLT) {
  384.         ads_fail(/*MSG3*/"Unable to establish digitizer pass-through mapping");
  385.         return RSERR;
  386.     }
  387.  
  388.     /*  Turn Tablet mode on  */
  389.  
  390.     tm.restype = RTSHORT;
  391.     tm.resval.rint = 1;
  392.     ads_setvar(/*MSG0*/"TABMODE", &tm);
  393.     ads_getvar(/*MSG0*/"TABMODE", &tm);
  394.     if (tm.resval.rint == 0) {
  395.         ads_fail(/*MSG4*/"Unable to turn on Tablet mode");
  396.         return RSERR;
  397.     }
  398.  
  399.     /*  Get the calibration points */
  400.  
  401.     ads_printf(/*MSG5*/"\nFour-point calibration for bilinear mapping...");
  402.     for (i = 0; i < 4; i++) {
  403.         tm.resval.rint = 1;
  404.         ads_setvar(/*MSG0*/"TABMODE", &tm);
  405.         sprintf(sb, /*MSG6*/"\nDigitize point #%d: ", i + 1);
  406.         if (ads_getpoint(NULL, sb, t[i]) != RTNORM)
  407.             return RSERR;
  408.         tm.resval.rint = 0;
  409.         ads_setvar(/*MSG0*/"TABMODE", &tm);   /* Turn Tablet mode off */
  410.         sprintf(sb, /*MSG7*/"\nEnter coordinates for point #%d: ", i + 1);
  411.         if (ads_getpoint(NULL, sb, p[i]) != RTNORM)
  412.             return RSERR;
  413.     }
  414.  
  415.     /*  Reorder the points to assure that the last two tablet points
  416.         have different Y-coordinates (to avoid some divisions by zero
  417.         in bilinmpt)  */
  418.  
  419.     if (t[2][Y] == t[3][Y]) {
  420.         if (t[1][Y] != t[3][Y]) {
  421.             pswap(t[1],t[2]);
  422.             pswap(p[1],p[2]);
  423.         } else if (t[0][Y] != t[3][Y]) {
  424.             pswap(t[0],t[2]);
  425.             pswap(p[0],p[2]);
  426.         } else {
  427.             ads_fail(/*MSG8*/"Degenerate calibration points");
  428.             return RSERR;
  429.         }
  430.     }
  431.  
  432.     /*  Figure out which sign to use in the solution of the quadratic
  433.         equation inside bilinmpt, by determining which choice most
  434.         nearly maps one of the calibration points correctly: */
  435.  
  436.     bilinsgn = 1;
  437.     ibilinmpt(t[0], p11);
  438.     bilinsgn = -1;
  439.     ibilinmpt(t[0], p12);
  440.     cpypoint(p0, p[0]);
  441.     p0[Z] = p11[Z] = p12[Z] = 0.0;
  442.     if (ads_distance(p11, p0) < ads_distance(p12, p0))
  443.         bilinsgn = 1;
  444.  
  445.     tm.resval.rint = 1;               /* Exit with Tablet mode ON */
  446.     ads_setvar(/*MSG0*/"TABMODE", &tm);
  447.  
  448.     cbrated = TRUE;
  449.     ads_retvoid();
  450.     return RSRSLT;
  451. }
  452.  
  453.  
  454. /*  BILINMPT  --  External function to perform the bilinear mapping on
  455.                   a point.  Expects a single ads_point as argument,
  456.                   returns an ads_point as value (if successful).  */
  457.  
  458. static int
  459. /*FCN*/bilinmpt()
  460. {
  461.     struct resbuf *args;
  462.     ads_point pprime;
  463.  
  464.     if (!cbrated) {
  465.         ads_fail(/*MSG9*/"Bilinear mapping isn't calibrated");
  466.         return RSERR;
  467.     }
  468.     args = ads_getargs();
  469.     if (args->rbnext != NULL ||
  470.         (args->restype != RTPOINT && args->restype != RT3DPOINT)) {
  471.         ads_fail(/*MSG10*/"Improper arguments to bilinmpt");
  472.         return RSERR;
  473.     }
  474.  
  475.     /*  Call function to do the actual mathematics */
  476.  
  477.     if (ibilinmpt(args->resval.rpoint, pprime) != RSRSLT) {
  478.         ads_fail(/*MSG11*/"bilinmpt can't transform point");
  479.         return RSERR;
  480.     } else {
  481.         pprime[Z] = 0.0;
  482.         ads_retpoint(pprime);
  483.         return RSRSLT;
  484.     }
  485. }
  486.  
  487.  
  488. /*  IBILINMPT  --  Perform the bilinear transformation on point "inpt",
  489.                    leaving the result in "pprime"  */
  490.  
  491. static int
  492. /*FCN*/ibilinmpt(inpt, pprime)
  493.   ads_real *inpt, *pprime;
  494. {
  495.     ads_real x, y, xb, yb, xt, yt, a, b, c, r1, r2;
  496.     ads_real x1, x2, x3, x4, y1, y2, y3, y4;
  497.  
  498.     x = inpt[X];
  499.     y = inpt[Y];
  500.  
  501.     /*  Ok, Mathematica time.  Pardon the dearth of details, and the
  502.         utter lack of simplification.  */
  503.  
  504.     /*  Put the four corners of the tablet quadrilateral in individual
  505.         variables (this was helpful during debugging)  */
  506.  
  507.     x1 = t[0][X];
  508.     x2 = t[1][X];
  509.     x3 = t[2][X];
  510.     x4 = t[3][X];
  511.     y1 = t[0][Y];
  512.     y2 = t[1][Y];
  513.     y3 = t[2][Y];
  514.     y4 = t[3][Y];
  515.  
  516.     /*  Compute the three coefficients in the quadratic equation whose
  517.         solution is yt */
  518.  
  519.     c = x2*y*y3*y3 - x4*y*y3*y3 - x*y2*y3*y3 + x4*y2*y3*y3 - x1*y*y3*y4 -
  520.         x2*y*y3*y4 + x3*y*y3*y4 + x4*y*y3*y4 + x*y1*y3*y4 - x4*y1*y3*y4 +
  521.         x*y2*y3*y4 - x3*y2*y3*y4 + x1*y*y4*y4 - x3*y*y4*y4 - x*y1*y4*y4 +
  522.         x3*y1*y4*y4;
  523.  
  524.     b = x1*y*y3 - x2*y*y3 - x3*y*y3 + x4*y*y3 - x*y1*y3 + x4*y1*y3 +
  525.         x*y2*y3 + x3*y2*y3 - 2*x4*y2*y3 + x*y3*y3 - x2*y3*y3 - x1*y*y4 +
  526.         x2*y*y4 + x3*y*y4 - x4*y*y4 + x*y1*y4 - 2*x3*y1*y4 + x4*y1*y4 -
  527.         x*y2*y4 + x3*y2*y4 - 2*x*y3*y4 + x1*y3*y4 + x2*y3*y4 + x*y4*y4 -
  528.         x1*y4*y4;   
  529.  
  530.     a = x3*y1 - x4*y1 - x3*y2 + x4*y2 - x1*y3 + x2*y3 + x1*y4 -
  531.         x2*y4;
  532.  
  533.     /*  The quadratic equation for yt has two solutions; the choice of
  534.         which to use is made externally by an appropriate setting of
  535.         bilinsgn.  */
  536.  
  537.     yt = (-b + bilinsgn * sqrt(b * b - 4.0 * a * c)) / (2.0 * a);
  538.  
  539.     /*  Fill in xb, yb, and xt.  When finished, (xb,yb) will be a point
  540.         on the lower edge of the quadrilateral, and (xt,yt) will be a
  541.         point on its upper edge.  They will divide their respective
  542.         edges in the same ratio, and inpt(x,y) will lie on the line
  543.         joining them.  */
  544.  
  545.     xb = (x2*y3 - x1*y4 + x1*yt - x2*yt) / (y3 - y4);
  546.     yb = (y2*y3 - y1*y4 + y1*yt - y2*yt) / (y3 - y4);
  547.     xt = (x4*y3 - x3*y4 + x3*yt - x4*yt) / (y3 - y4); 
  548.     if (x2 == x1)
  549.         r1 = (y2 - yb) / (y2 - y1);
  550.     else
  551.         r1 = (x2 - xb) / (x2 - x1);
  552.     if (xt == xb)
  553.         r2 = (yt - y) / (yt - yb);
  554.     else
  555.         r2 = (xt - x) / (xt - xb);
  556.     
  557.     /*  The r1 and r2 are the "Coon's patch" coordinates of the input
  558.         point; transfer them to the output.  */
  559.  
  560.     xb = r1 * p[0][X] + (1 - r1) * p[1][X];
  561.     yb = r1 * p[0][Y] + (1 - r1) * p[1][Y];
  562.     xt = r1 * p[2][X] + (1 - r1) * p[3][X];
  563.     yt = r1 * p[2][Y] + (1 - r1) * p[3][Y];
  564.     pprime[X] = r2 * xb + (1 - r2) * xt;
  565.     pprime[Y] = r2 * yb + (1 - r2) * yt;
  566.     return RSRSLT;
  567. }
  568.  
  569.  
  570.  
  571. /*  PSWAP  --  Swap two 2D points */
  572.  
  573. static void
  574. /*FCN*/pswap(p1, p2)
  575.   ads_real *p1, *p2;
  576. {
  577.     ads_real t;
  578.  
  579.     t = p1[X];
  580.     p1[X] = p2[X];
  581.     p2[X] = t;
  582.     t = p1[Y];
  583.     p1[Y] = p2[Y];
  584.     p2[Y] = t;
  585. }
  586.  
  587.  
  588.  
  589. /*  SAMPLE  --  Draw a sample figure for exercising the bilinear
  590.                 mapping.  This function is not religious about
  591.                 restoring system variables to previous settings.  */
  592.  
  593. static int
  594. /*FCN*/sample()
  595. {
  596.     char junk[20];
  597.     struct resbuf svb;
  598.     int i;
  599.     static char *msg[] = {
  600.         "",
  601.         /*MSG12*/"We will now draw a sample figure which you can use to test",
  602.         /*MSG13*/"bilinear tablet transformations.  When complete, plot this",
  603.         /*MSG14*/"figure and attach it to your tablet surface.  Replace one of",
  604.         /*MSG15*/"the items in your BUTTONS menu with the following:",
  605.         "",
  606.         /*MSG0*/"^P(bilinmpt (getpoint)) \\^P",
  607.         "",
  608.         /*MSG16*/"Then enter BILINCAL, and when you are prompted for points,",
  609.         /*MSG17*/"digitize (with the normal pick button) the corners of the",
  610.         /*MSG18*/"quadrilateral in this order:",
  611.         "",
  612.         /*MSG19*/"   lower left, lower right, upper left, upper right",
  613.         "",
  614.         /*MSG20*/"Give them coordinates of (0,0), (16,0), (0,16), and (16,16),",
  615.         /*MSG21*/"respectively.  Then, using the button to which you attached",
  616.         /*MSG22*/"the above menu item, digitize lines connecting the four",
  617.         /*MSG23*/"corners of the quadrilateral.  To see what this transformation",
  618.         /*MSG24*/"does to straight lines, enter a polyline following the curved",
  619.         /*MSG25*/"path in the figure and another polyline along the straight", 
  620.         /*MSG26*/"diagonal path.",
  621.         "",
  622.         NULL
  623.     };
  624.  
  625.     /*  Print the message */
  626.  
  627.     ads_textscr();
  628.     for (i = 0; msg[i] != NULL; i++)
  629.         ads_printf("%s\n", msg[i]);
  630.     if (ads_getstring(0, /*MSG27*/"Press RETURN to continue: ", junk) != RTNORM)
  631.         return RSERR;
  632.  
  633.     /*  Draw the figure */
  634.  
  635.     ads_graphscr();
  636.     ads_getvar(/*MSG0*/"CMDECHO", &svb);
  637.     svb.resval.rint = 0;
  638.     ads_setvar(/*MSG0*/"CMDECHO", &svb);
  639.          
  640.     ads_command(RTSTR, /*MSG0*/"_.ZOOM", RTSTR, /*MSG0*/"_W", RTSTR, "-10,0",
  641.                 RTSTR, "1.5,12", RTNONE);
  642.  
  643.     dline("1.0,1.0", "-7.0,1.0");
  644.     dline("-7.0,1.0", "-9.0,8.0");
  645.     dline("-9.0,8.0", "-6.0,11.0");
  646.     dline("-6.0,11.0", "1.0,1.0");
  647.     dline("0.5,1.0", "-6.1875,10.8125");
  648.     dline("-6.375,10.625", "0.0,1.0");
  649.     dline("-0.5,1.0", "-6.5625,10.4375");
  650.     dline("-6.75,10.25", "-1.0,1.0");
  651.     dline("-1.5,1.0", "-6.9375,10.0625");
  652.     dline("-7.125,9.875", "-2.0,1.0");
  653.     dline("-2.5,1.0", "-7.3125,9.6875");
  654.     dline("-7.5,9.5", "-3.0,1.0");
  655.     dline("-3.5,1.0", "-7.6875,9.3125");
  656.     dline("-7.875,9.125", "-4.0,1.0");
  657.     dline("-4.5,1.0", "-8.0625,8.9375");
  658.     dline("-8.25,8.75", "-5.0,1.0");
  659.     dline("-5.5,1.0", "-8.4375,8.5625");
  660.     dline("-8.625,8.375", "-6.0,1.0");
  661.     dline("-6.5,1.0", "-8.8125,8.1875");
  662.     dline("0.5625,1.625", "-7.125,1.4375");
  663.     dline("-7.25,1.875", "0.125,2.25");
  664.     dline("-0.3125,2.875", "-7.375,2.3125");
  665.     dline("-7.5,2.75", "-0.75,3.5");
  666.     dline("-1.1875,4.125", "-7.625,3.1875");
  667.     dline("-7.75,3.625", "-1.625,4.75");
  668.     dline("-2.0625,5.375", "-7.875,4.0625");
  669.     dline("-8.0,4.5", "-2.5,6.0");
  670.     dline("-2.9375,6.625", "-8.125,4.9375");
  671.     dline("-8.25,5.375", "-3.375,7.25");
  672.     dline("-3.8125,7.875", "-8.375,5.8125");
  673.     dline("-8.5,6.25", "-4.25,8.5");
  674.     dline("-4.6875,9.125", "-8.625,6.6875");
  675.     dline("-8.75,7.125", "-5.125,9.75");
  676.     dline("-5.5625,10.375", "-8.875,7.5625");
  677.  
  678.     ads_command(RTSTR,/*MSG0*/"_.PLINE", RTNONE);
  679.     dvert("-7.0,1.0");
  680.     dvert("-6.7636,1.2868");
  681.     dvert("-6.6445,1.4492");
  682.     dvert("-6.3673,1.8608");
  683.     dvert("-6.3281,1.9219");
  684.     dvert("-6.1843,2.1674");
  685.     dvert("-6.0508,2.418");
  686.     dvert("-5.926,2.6764");
  687.     dvert("-5.8125,2.9375");
  688.     dvert("-5.7069,3.2089");
  689.     dvert("-5.6133,3.4805");
  690.     dvert("-5.5271,3.7649");
  691.     dvert("-5.4531,4.0469");
  692.     dvert("-5.3866,4.3442");
  693.     dvert("-5.332,4.6367");
  694.     dvert("-5.2853,4.947");
  695.     dvert("-5.25,5.25");
  696.     dvert("-5.2231,5.573");
  697.     dvert("-5.207,5.8867");
  698.     dvert("-5.2001,6.2224");
  699.     dvert("-5.2031,6.5469");
  700.     dvert("-5.2163,6.8951");
  701.     dvert("-5.2383,7.2305");
  702.     dvert("-5.2716,7.5911");
  703.     dvert("-5.3125,7.9375");
  704.     dvert("-5.3659,8.3105");
  705.     dvert("-5.4258,8.668");
  706.     dvert("-5.4993,9.0532");
  707.     dvert("-5.5781,9.4219");
  708.     dvert("-5.5911,9.477");
  709.     dvert("-5.7695,10.1992");
  710.     dvert("-5.8442,10.4783");
  711.     dvert("-6.0,11.0");
  712.     ads_command(RTSTR, "", RTNONE);
  713.  
  714.     dline("-7.0,1.0", "-6.0,11.0");   /* Draw the "diagonal" */
  715.  
  716.     svb.resval.rint = 1;  
  717.     ads_setvar(/*MSG0*/"CMDECHO", &svb);      /* Turn command echo back on. */
  718.  
  719.     ads_retvoid();
  720.     return RSRSLT;
  721. }
  722.  
  723.  
  724. /*  DLINE  --  Draw a line.  */
  725.  
  726. static void
  727. /*FCN*/dline(p1str, p2str)
  728.   char *p1str, *p2str;
  729. {
  730.     ads_command(RTSTR, /*MSG0*/"_.LINE", RTSTR, p1str, RTSTR, p2str, RTSTR, "",
  731.                 RTNONE);
  732. }
  733.  
  734. /*  DVERT  --  Draw one vertex of a Polyline */
  735.  
  736. static void
  737. /*FCN*/dvert(p1str)
  738.   char *p1str;
  739. {
  740.     ads_command(RTSTR, p1str, RTNONE);
  741. }
  742.  
  743.  
  744. /*  SAVCAL  --  External function to save the current tablet calibration
  745.                 locally.  */
  746.  
  747. static int
  748. /*FCN*/savcal()
  749. {
  750.     int stat;
  751.     struct resbuf *rb;
  752.  
  753.     /*  Free any previously saved calibration  */
  754.     if (tcal != NULL) {
  755.         ads_relrb(tcal);
  756.         tcal = NULL;
  757.     }
  758.  
  759.     /*  Build argument list to request current calibration from 
  760.         ads_tablet()  */
  761.     rb = ads_buildlist(RTSHORT, 0, RTNONE);
  762.     stat = ads_tablet(rb, &tcal);
  763.     ads_relrb(rb);
  764.  
  765.     if (stat == RTNORM)
  766.         stat = RSRSLT;
  767.     else {
  768.         aperror(/*MSG0*/"ads_tablet");
  769.         stat = RSERR;
  770.     }
  771.     ads_retvoid();
  772.     return stat;
  773. }
  774.  
  775.  
  776. /*  RSTCAL  --  External function to restore a tablet calibration
  777.                 previously saved by savcal().  */
  778.  
  779. static int
  780. /*FCN*/rstcal()
  781. {
  782.     int stat = RSERR;
  783.  
  784.     if (tcal == NULL)
  785.         ads_fail(/*MSG28*/"rstcal: no calibration saved by savcal\n");
  786.     else if (ads_tablet(tcal, NULL) != RTNORM)
  787.         aperror(/*MSG0*/"ads_tablet");
  788.     else
  789.         stat = RSRSLT;
  790.     ads_retvoid();
  791.     return stat;
  792. }
  793.  
  794.  
  795. /*  APERROR  --  Poor brother to ads_perr:  display the value of
  796.                  errno associated with a given ADS routine.  */
  797. static void
  798. /*FCN*/aperror(routine)
  799.   char *routine;
  800. {
  801.     struct resbuf gvb;
  802.     char msgbuf[60];
  803.  
  804.     ads_getvar(/*MSG0*/"ERRNO", &gvb);
  805.     if (routine == NULL)
  806.         sprintf(msgbuf, /*MSG29*/"Error %d", gvb.resval.rint);
  807.     else
  808.         sprintf(msgbuf, /*MSG30*/"Error %d from %s", gvb.resval.rint, routine);
  809.     ads_fail(msgbuf);
  810. }
  811.